#pragma once
#ifndef _KH_ENGINE_ENGINE_H
#define _KH_ENGINE_ENGINE_H
#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Units.h>
#include <Kharlia/Engine/Level.h>
#include <Kharlia/Engine/TileData.h>
#include <Kharlia/Engine/Net.h>
#include <Kharlia/Engine/Resource.h>
#include <Kharlia/Core/Logger.h>
#include <Kharlia/Core/Subsystem.h>

#include <CEGUIDefaultResourceProvider.h>

namespace kharlia { namespace engine {

class CActor;
class CWorld;
class CPlayer;
class CClient;

void _InitEngineClasses();

// TODO: Organize the public members for CEngine

class KH_ENGINE_DECLSPEC CEngine: public core::CSubsystem {
    shared_ptr<core::FLogger> mLogger;
    bool mInited;
    bool mInitedClient;
    bool mIsServer;
    bool mIsClient;
    bool mIsEditor;
    float mTickElapsed;
    shared_ptr<CWorld> mWorld;
    object mConfig;
    shared_ptr<CPlayer> mLocalPlayer;
    shared_ptr<FResourceManager> mResourceManager;
    shared_ptr<CClient> mClient;
    uint16 mClientTickRate;
    float mClientTickElapsed;
    // InputTarget

    // Editor
    bool mEditorEnabled; // toggle for editor functionality
    bool mEditorClampBrushPos; // true if brush position is clamped to tiles (floored)
    shared_ptr<FTileData> mEditorBrush;
    bool mEditorBrushVisible;
    FVector2F mEditorBrushPos;
    bool mEditorGridVisible;
    FRectF mEditorSelection;
    bool mEditorSelectionVisible;
    shared_ptr<FTileLayer> mEditorBrushLayer;

    static CEngine* msInstance;

    KH_DECL_GC()

public:
    CEngine(PyObject* self);
    virtual ~CEngine();

    void Init();
    void _Init();
    void Exit();
    void _Exit();
    void InitClient(object viewport);
    void FiniClient();
    virtual void Tick(float deltaTime);
    shared_ptr<CWorld> LoadWorld(object path);
    bool EnsureLocalPlayer();

    KH_DEF_GET(const shared_ptr<core::FLogger>&, Logger)
    KH_DEF_GET(bool, Inited)
    KH_DEF_GET(bool, InitedClient)
    KH_DEF_GETSET(bool, IsServer)
    KH_DEF_GETSET(bool, IsClient)
    KH_DEF_GETSET(bool, IsEditor)
    KH_DEF_GETSET(shared_ptr<CWorld>, World)
    KH_DEF_GETSET(object, Config)
    KH_DEF_GET(const shared_ptr<CPlayer>&, LocalPlayer)
    KH_DEF_GET(const shared_ptr<FResourceManager>&, ResourceManager)
    KH_DEF_GET(const shared_ptr<CClient>&, Client)
    KH_DEF_GETSET(uint16, ClientTickRate)

    virtual void OnNetClientConnected(shared_ptr<CWorld>);


    // Editor
    KH_DEF_GET(bool, EditorEnabled)
    inline void SetEditorEnabled(bool value) {
        mEditorEnabled = mIsEditor = value;
    }
    // Editor Brush
    KH_DEF_GET(const shared_ptr<FTileData>&, EditorBrush)
    inline void SetEditorBrush(const shared_ptr<FTileData>& b);
    inline const shared_ptr<FTileLayer>& GetEditorBrushLayer() const {
        return mEditorBrushLayer;
    }
    KH_DEF_GETSET(bool, EditorBrushVisible)
    KH_DEF_GET(const FVector2F&, EditorBrushPos)
    inline void SetEditorBrushPos(const FVector2F& p) {
        mEditorBrushPos = p;
        if (mEditorClampBrushPos) {
            mEditorBrushPos.X = floor(mEditorBrushPos.X);
            mEditorBrushPos.Y = floor(mEditorBrushPos.Y);
        }
    }
    // Editor Selection
    KH_DEF_GETSET(const FRectF&, EditorSelection)
    KH_DEF_GETSET(bool, EditorSelectionVisible)
    KH_DEF_GETSET(bool, EditorGridVisible)
    //void SetEditorSelectionPy(tuple position);
    //void SetEditorSelection(FVector2I topleft, FVector2I botright);

    static shared_ptr<CEngine> GetInstance();
    static CEngine* GetInstancePtr();

    friend void _InitEngineClasses();
    friend void _InitEngineBindings();
    friend struct FEngineRenderInfo;
};

inline void CEngine::Init() {
    call_method<void>(mSelf, "Init");
}

inline void CEngine::Exit() {
    call_method<void>(mSelf, "Exit");
}

inline shared_ptr<CEngine> CEngine::GetInstance() {
    assert(msInstance);
    return msInstance->GetSelfPtr<CEngine>();
}

inline CEngine* CEngine::GetInstancePtr() {
    return msInstance;
}

}} //namespace Kharlia

KH_HAS_BACK_REFERENCE(kharlia::engine::CEngine)

#endif
